﻿namespace PugLib.Extensions
{
    using System;
    using System.Globalization;

    public static class DateTimes
    {
        /// <summary>
        /// The ISO 8601 format string.
        /// </summary>
        public const string Iso8601Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'";

        public static DateTime HoursAgo(this DateTime input, double number)
        {
            long ticksAgo = Convert.ToInt64(TimeSpan.TicksPerHour * number);
            return new DateTime(input.Ticks - ticksAgo);
        }

        public static DateTime HoursFromNow(this DateTime input, double number)
        {
            long ticksFromNow = Convert.ToInt64(TimeSpan.TicksPerHour * number);
            return new DateTime(input.Ticks + ticksFromNow);
        }

        public static string ConvertToRelativeTime(this DateTime date)
        {
            string relative = string.Empty;
            int hour = date.Hour;
            string meridian = date.Hour > 12 ? "pm" : "am";

            if (date.Minute < 8)
            {
                relative = "about";
            }

            else if (date.Minute > 52)
            {
                relative = "about";
                hour++;
            }
            else if (date.Minute < 22)
            {
                relative = "a quarter past";
            }
            else if (date.Minute < 38)
            {
                relative = "half past";
            }
            else if (date.Minute <= 52)
            {
                relative = "a quarter to";
                if (hour == 23)
                {
                    hour = 0;
                    meridian = string.Empty;
                }
                else
                {
                    hour++;
                }
            }

            if (hour == 0)
            {
                meridian = string.Empty;
            }
            string name = GetHourText(hour);
            return relative + " " + name + " " + meridian;
        }

        public static string GetHourText(int hour)
        {
            if (hour > 12)
            {
                hour = hour - 12;
            }
            switch (hour)
            {
                case 1:
                    return "one";
                case 2:
                    return "two";
                case 3:
                    return "three";
                case 4:
                    return "four";
                case 5:
                    return "five";
                case 6:
                    return "six";
                case 7:
                    return "seven";
                case 8:
                    return "eight";
                case 9:
                    return "nine";
                case 10:
                    return "ten";
                case 11:
                    return "eleven";
                default:
                    if (hour == 0)
                    {
                        return "midnight";
                    }
                    else
                    {
                        return "twelve";
                    }
            }
        }


        public static string TimeAgo(this DateTime date)
        {
            TimeSpan timeSince = DateTime.Now.Subtract(date);

            if (timeSince.TotalMilliseconds < 1)
            {
                return "not yet";
            }
            if (timeSince.TotalMinutes < 1)
            {
                return "just now";
            }
            if (timeSince.TotalMinutes < 2)
            {
                return "1 minute ago";
            }
            if (timeSince.TotalMinutes < 60)
            {
                return string.Format(CultureInfo.InvariantCulture, "{0} minutes ago", timeSince.Minutes);
            }
            if (timeSince.TotalMinutes < 120)
            {
                return "1 hour ago";
            }
            if (timeSince.TotalHours < 24)
            {
                return string.Format(CultureInfo.InvariantCulture, "{0} hours ago", timeSince.Hours);
            }
            if (timeSince.TotalDays == 1)
            {
                return "yesterday";
            }
            if (timeSince.TotalDays < 7)
            {
                return string.Format(CultureInfo.InvariantCulture, "{0} days ago", timeSince.Days);
            }
            if (timeSince.TotalDays < 14)
            {
                return "last week";
            }
            if (timeSince.TotalDays < 21)
            {
                return "2 weeks ago";
            }
            if (timeSince.TotalDays < 28)
            {
                return "3 weeks ago";
            }
            if (timeSince.TotalDays < 60)
            {
                return "last month";
            }
            if (timeSince.TotalDays < 365)
            {
                return string.Format(CultureInfo.InvariantCulture, "{0} months ago", Math.Round(timeSince.TotalDays / 30));
            }
            if (timeSince.TotalDays < 730)
            {
                return "last year";
            }
            //last but not least...
            return string.Format(CultureInfo.InvariantCulture, "{0} years ago", Math.Round(timeSince.TotalDays / 365));
        }

        public static float WeekendsBetween(this DateTime early, DateTime later)
        {
            // this function was stolen from a python-based program on the interweb.
            // http://dirtsimple.org/2004/10/nineteen-hundred-weekends.html
            DateTime start;
            DateTime end;
            if (later < early)
            {
                end = early;
                start = later;
            }
            else
            {
                end = later;
                start = early;
            }

            int daysBetween = (end - start).Days;
            float weekends = (daysBetween / 7);
            int leftover = (daysBetween % 7);
            if (leftover > 0)
            {
                int startDay = (int)(end.AddDays(-1 * leftover)).DayOfWeek;
                if (startDay == 0)
                {
                    startDay = 7; // .net uses 0 for sunday, python used iso, so 7 for sunday
                }
                int endDay = startDay + leftover;
                if (startDay <= 6 && endDay > 6)
                {
                    weekends += .5f;
                }
                if (startDay <= 7 && endDay > 7)
                {
                    weekends += .5f;
                }
            }
            return weekends;
        }

        /// <summary>
        /// Converts the specified ISO 8601 representation of a date and time
        /// to its DateTime equivalent.
        /// </summary>
        /// <param name="value">The ISO 8601 string representation to parse.</param>
        /// <returns>The DateTime equivalent.</returns>
        public static DateTime ParseIso8601(string value)
        {
            return DateTime.ParseExact(value,
                                       Iso8601Format, CultureInfo.InvariantCulture,
                                       DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
        }


        /// <summary>
        /// Formats the date in the standard ISO 8601 format.
        /// </summary>
        /// <param name="value">The date to format.</param>
        /// <returns>The formatted date.</returns>
        public static string ToIso8601(this DateTime value)
        {
            return value.ToUniversalTime().ToString(Iso8601Format, CultureInfo.InvariantCulture);
        }
    }
}